#!/bin/bash
VERSION='2.0'

GNAP_LIBDIR=${GNAP_LIBDIR:-'/usr/lib/gnap'}
source ${GNAP_LIBDIR}/gnap_shared.sh
echo "GNAP Core Building tool ${NAME} version ${VERSION}"

usage() {
	echo 'Options:'
	echo '    -t stage             Build stage to execute (or "all")'
	echo '    -o overlay_dir       Portage overlay directory'
	echo '    -v version_stamp     GNAP timestamp'
	echo '    -f                   Force all answers to yes'
	echo '    -s seedstagefile     File to use as a Stage3 seed tarball'
	echo '    -p portage_snapshot  File to use as Portage snapshot'
	echo '    -l logfile           Use specific log file prefix'
	echo '    -c catalyst.conf     Use specific catalyst.conf file'
	echo '    -e specs             Specs directory or tar.bz2 file'
	echo
	echo "Please use man ${NAME} for more details."
}

if [[ "$#" -eq 0 ]]; then
	usage
	exit 0
fi

# Catalyst executable and config file
GNAP_CATALYST_BIN=${GNAP_CATALYST_BIN:-"/usr/bin/catalyst"}
GNAP_CATALYST_CONF=${GNAP_CATALYST_CONF:-"/etc/catalyst/catalyst.conf"}

# Default options
GNAP_STAMP=${GNAP_STAMP:-$(date +%Y%m%d)}
GNAP_STAGE3=${GNAP_STAGE3:-0}
GNAP_LIVECD1=${GNAP_LIVECD1:-0}
GNAP_LIVECD2=${GNAP_LIVECD2:-0}
GNAP_TARBALL=${GNAP_TARBALL:-0}
GNAP_MODULES=${GNAP_MODULES:-0}

# Read options (phase 1)
gbegin 'Checking parameters'

OPTIND=0
while getopts ':hs:p:m:o:v:t:fl:c:e:' option; do
	case ${option} in
		h ) 
			gtest
			usage
			exit 0;;
		e ) GNAP_SPECS="${OPTARG}";;
		s|p|o|v|t|f|l|c ) :;;
		* ) gtest 1 'Specified options are incomplete or unknown!';;
	esac
done

# Root is needed
# * non-root can only use "gnap_make -h"
test "${EUID}" -eq 0
gtest continued $? "You need to be root to run ${NAME}"

# Setting up temporary directory
TEMPDIR=$(mktemp -d -t gnap_make.XXXXXX)
gtest continued $? 'Failed to create temporary directory'

# Prepare specs dir and check common.conf file
SPECDIR="${TEMPDIR}/specs"
if [[ -f "${GNAP_SPECS}" ]]; then
	gmkdir "${SPECDIR}"
	tar jx -f "${GNAP_SPECS}" -C "${SPECDIR}"
	gtest continued $? 'Failed to unpack specs'
elif [[ -d "${GNAP_SPECS}" ]]; then
	cp -rp "${GNAP_SPECS}" "${SPECDIR}"
	gtest continued $? 'Failed to copy specs directory contents'
else
	gtest continued 1 "${GNAP_SPECS} not found, provide a valid -e option"
fi
test -f "${SPECDIR}/common.conf"
gtest continued $? "Incorrect specdir: ${SPECDIR}/common.conf not found!"
source "${SPECDIR}/common.conf"
export CHOST
export CFLAGS
export CXXFLAGS
if [[ -n "${DISTCC_HOSTS}" ]]; then
	DISTCCSPEC="distcc_hosts: ${DISTCC_HOSTS}"
fi


# Read options (phase 2)
OPTIND=0
while getopts ':hs:p:m:o:v:t:fl:c:e:' option; do
	case ${option} in
		c ) GNAP_CATALYST_CONF="${OPTARG}";;
		s|p|o|v|t|f|l|e|h ) :;;
		* ) gtest 1 'Specified options are incomplete or unknown!';;
	esac
done

# catalyst.conf file
test -f "${GNAP_CATALYST_CONF}"
gtest continued $? "${GNAP_CATALYST_CONF} file not found!"
source "${GNAP_CATALYST_CONF}"

# Read options (phase 3)
OPTIND=0
while getopts ':hs:p:m:o:v:t:fl:c:e:' option; do
	case ${option} in
		s ) GNAP_STAGE3FILE="${OPTARG}";;
		p ) GNAP_SNAPSHOTFILE="${OPTARG}";;
		o ) GNAP_PORTAGE_OVERLAYS="${GNAP_PORTAGE_OVERLAYS} ${OPTARG}";;
		v ) GNAP_STAMP="${OPTARG}";;
		t )
			case "${OPTARG}" in
				all )
					GNAP_STAGE3=1
					GNAP_LIVECD1=1
					GNAP_LIVECD2=1
					GNAP_TARBALL=1
					GNAP_MODULES=1;;
				stage3 ) GNAP_STAGE3=1;;
				livecd-stage1 ) GNAP_LIVECD1=1;;
				livecd-stage2 ) GNAP_LIVECD2=1;;
				tarball ) GNAP_TARBALL=1;;
				extensions ) GNAP_MODULES=1;;
				* ) gtest 1 'Specified stage is unknown!';;
			esac;;
		f ) GNAP_FORCEYES=1;;
		l ) GNAP_LOGPREFIX="${OPTARG}";;
		c|e|h ) :;;
		* ) gtest 1 'Specified options are incomplete or unknown!';;
	esac
done

# At least one target is needed
test "${GNAP_STAGE3}" -eq 0 &&
test "${GNAP_LIVECD1}" -eq 0 &&
test "${GNAP_LIVECD2}" -eq 0 &&
test "${GNAP_TARBALL}" -eq 0 &&
test "${GNAP_MODULES}" -eq 0
if [[ $? -eq 0 ]]; then
	gtest continued 1 \
	'No target specified. You should provide at least one -t option.'
fi

NEEDS_SNAPSHOT=0
test "${GNAP_STAGE3}" -eq 1 ||
test "${GNAP_LIVECD1}" -eq 1 ||
test "${GNAP_LIVECD2}" -eq 1 ||
test "${GNAP_MODULES}" -eq 1
if [[ $? -eq 0 ]]; then
	NEEDS_SNAPSHOT=1
fi

# storedir must exist
gmkdir "${storedir}"

# Stage3 needs a seed stage
if [[ "${GNAP_STAGE3}" -eq 1 ]]; then
	test -f "${GNAP_STAGE3FILE}"
	gtest continued $? 'The "-s" option needs to designate a valid seed stage'
fi

# Snapshot must exist if a stage is selected
if [[ "${NEEDS_SNAPSHOT}" -eq 1 ]]; then
	test -f "${GNAP_SNAPSHOTFILE}"
	gtest continued $? "Can't find ${GNAP_SNAPSHOTFILE}"
fi

# Seed stage if needed must be an existing file
if [[ "${GNAP_STAGE3}" -eq 1 ]]; then
	test -f "${GNAP_STAGE3FILE}"
	gtest continued $? "${GNAP_STAGE3FILE} is not a valid stage3 tarball"
fi

# If extensions and no stage3, warn that we'll use seedstage as stage3
STAGE3LOC="${storedir}/builds/${RELTYPE}/stage3-${SUBARCH}-${GNAP_STAMP}.tar.bz2"
if [[ "${GNAP_MODULES}" -eq 1 || "${GNAP_LIVECD1}" -eq 1 ]]; then
	if [[ "${GNAP_STAGE3}" -ne 1 && ! -f "${STAGE3LOC}" ]]; then
		gwarn '"livecd-stage1" or "extensions" was selected without "stage3".'
		gconfirm 'Should I use the seed stage as stage3 result?'
		gmkdir "${storedir}/builds/${RELTYPE}"
		cp "${GNAP_STAGE3FILE}" "${STAGE3LOC}"
	fi
fi

# Explain what will get built
if [[ "${GNAP_STAGE3}" -eq 1 ]]; then
	TARGETLIST='[stage3] '
fi
if [[ "${GNAP_LIVECD1}" -eq 1 ]]; then
	TARGETLIST="${TARGETLIST}[livecd-stage1] "
fi
if [[ "${GNAP_LIVECD2}" -eq 1 ]]; then
	TARGETLIST="${TARGETLIST}[livecd-stage2] "
fi
if [[ "${GNAP_TARBALL}" -eq 1 ]]; then
	TARGETLIST="${TARGETLIST}[tarball] "
fi
if [[ "${GNAP_MODULES}" -eq 1 ]]; then
	TARGETLIST="${TARGETLIST}[extensions]"
fi
ginfo 'The following targets will be called:'
ginfo "${TARGETLIST}"

# Confirm tarball overwrite if TARBALL stage selected
if [[ "${GNAP_TARBALL}" -eq 1 && -e "gnap-${VERSION}-${GNAP_STAMP}.tar" ]]; then
	gconfirm "gnap-${VERSION}-${GNAP_STAMP}.tar already exists, overwrite"
fi

# Logfile setup and confirmation
if [[ -z "$GNAP_LOGPREFIX" ]]; then
	GNAP_LOGPREFIX="./${NAME}-${GNAP_STAMP}"
fi

if [[ -f "${GNAP_LOGPREFIX}.out" || -f "${GNAP_LOGPREFIX}.err" ]]; then
	if [[ "${GNAP_FORCEYES}" -ne 1 ]]; then
		read -ep \
			" ${W}*${N} Logfile(s) already exists. Append/Overwrite [A]: " \
			answer
		if [[ "${answer}" == 'o' || "${answer}" == 'O' ]]; then
			rm "${GNAP_LOGPREFIX}.out" "${GNAP_LOGPREFIX}.err"
		fi
	fi
fi
touch "${GNAP_LOGPREFIX}.out"
touch "${GNAP_LOGPREFIX}.err"
SEELOGFILES="see ${GNAP_LOGPREFIX}.err and .out for details"

# Snapshot preparation
if [[ "${NEEDS_SNAPSHOT}" -eq 1 ]]; then
	gbegin 'Preparing portage snapshot'

	gmkdir "${storedir}/snapshots"

	if [[ -z "${GNAP_PORTAGE_OVERLAYS}" ]]; then
		cp "${GNAP_SNAPSHOTFILE}" "${storedir}/snapshots/portage-${GNAP_STAMP}.tar.bz2"
		gtest $? "Snapshot preparation failed, ${SEELOGFILES}"
	else
		TEMPPRTDIR="${TEMPDIR}/portage"
		gmkdir "${TEMPPRTDIR}"

		tar jxf "${GNAP_SNAPSHOTFILE}" -C "${TEMPPRTDIR}" \
			>> "${GNAP_LOGPREFIX}.out" 2>> "${GNAP_LOGPREFIX}.err"

		for overlay in ${GNAP_PORTAGE_OVERLAYS} ; do
			cp -rp ${overlay}/* "${TEMPPRTDIR}/portage"
			gtest continued $? "Failed to copy ${overlay}"
		done

		tar jcf "${storedir}/snapshots/portage-${GNAP_STAMP}.tar.bz2" \
			-C "${TEMPPRTDIR}" . \
			>> "${GNAP_LOGPREFIX}.out" 2>> "${GNAP_LOGPREFIX}.err"
		gtest $? "Snapshot preparation failed, ${SEELOGFILES}"
	fi
fi

# Stage3 phase
if [[ "${GNAP_STAGE3}" -eq 1 ]]; then
	gbegin "${G}[stage3]${N} stage (base system build)"

	gmkdir "${storedir}/builds/${RELTYPE}"
	cp "${GNAP_STAGE3FILE}" "${storedir}/builds/${RELTYPE}/seedstage.tar.bz2"

	TEMPCONF="${TEMPDIR}/stage3.conf"
	touch "${TEMPCONF}"
	gtest continued $? 'Failed to create stage3 temporary conf file'
	cat >> "${TEMPCONF}" <<EOF
target: stage3
${DISTCCSPEC}
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${GNAP_STAMP}
version_stamp: ${GNAP_STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/seedstage
portage_confdir: ${SPECDIR}/portage_confdir
EOF
	$GNAP_CATALYST_BIN -c "${GNAP_CATALYST_CONF}" -f "${TEMPCONF}" \
		>> "${GNAP_LOGPREFIX}.out" 2>> "${GNAP_LOGPREFIX}.err"
	gtest $? "[stage3] failed, ${SEELOGFILES}"
	rm "${storedir}/builds/${RELTYPE}/seedstage.tar.bz2"
fi

# LIVECD-STAGE1 phase
if [[ "${GNAP_LIVECD1}" -eq 1 ]]; then
	gbegin "${G}[livecd-stage1]${N} stage (GNAP-specific packages build)"
	TEMPCONF="${TEMPDIR}/livecd-stage1.conf"
	touch "${TEMPCONF}"
	gtest continued $? 'Failed to create livecd-stage1 temporary conf file'
	cat >> "${TEMPCONF}" <<EOF
target: livecd-stage1
${DISTCCSPEC}
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${GNAP_STAMP}
version_stamp: ${GNAP_STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/stage3-${SUBARCH}-${GNAP_STAMP}
portage_confdir: ${SPECDIR}/portage_confdir
EOF
	cat "${SPECDIR}/packages.conf" >> "${TEMPCONF}"

	$GNAP_CATALYST_BIN -c "${GNAP_CATALYST_CONF}" -f "${TEMPCONF}" \
		>> "${GNAP_LOGPREFIX}.out" 2>> "${GNAP_LOGPREFIX}.err"
	gtest $? "[livecd-stage1] failed, ${SEELOGFILES}"
fi

# LIVECD-STAGE2 phase
if [[ "${GNAP_LIVECD2}" -eq 1 ]]; then
	gbegin "${G}[livecd-stage2]${N} stage (kernel and LiveCD builds)"
	TEMPCONF="${TEMPDIR}/livecd-stage2.conf"
	touch "${TEMPCONF}"
	gtest continued $? 'Failed to create livecd-stage2 temporary conf file'
	cat >> "${TEMPCONF}" <<EOF
target: livecd-stage2
${DISTCCSPEC}
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${GNAP_STAMP}
version_stamp: ${GNAP_STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/livecd-stage1-${SUBARCH}-${GNAP_STAMP}
boot/kernel/gentoo/sources: ${KERNEL_SOURCES}
boot/kernel/gentoo/config: ${SPECDIR}/kernel.config
boot/kernel/gentoo/extraversion: GNAP-${VERSION}
livecd/iso: gnap-${VERSION}-${GNAP_STAMP}.iso
livecd/fsscript: ${SPECDIR}/fsscript
livecd/root_overlay: ${SPECDIR}/root_overlay
EOF
	cat "${SPECDIR}/livecd.conf" >> "${TEMPCONF}"

	$GNAP_CATALYST_BIN -c "${GNAP_CATALYST_CONF}" -f "${TEMPCONF}" \
		>> "${GNAP_LOGPREFIX}.out" 2>> "${GNAP_LOGPREFIX}.err"
	gtest $? "[livecd-stage2] failed, ${SEELOGFILES}"
fi

# TARBALL phase
if [[ "${GNAP_TARBALL}" -eq 1 ]]; then
	gbegin "${G}[tarball]${N} phase (Creation of core and basefs components)"
	test -e "gnap-${VERSION}-${GNAP_STAMP}.iso"
	gtest continued $? "No gnap-${VERSION}-${GNAP_STAMP}.iso file to convert !"
	test -d "${storedir}/tmp/gnap/livecd-stage1-${SUBARCH}-${GNAP_STAMP}"
	gtest $? 'Missing livecd-stage2 results'

	gbegin '  Creating core component'
	TEMPMNTDIR="${TEMPDIR}/mount"
	gmkdir "${TEMPMNTDIR}"
	TEMPISODIR="${TEMPDIR}/iso"
	gmkdir "${TEMPISODIR}"

	mount -o loop "gnap-${VERSION}-${GNAP_STAMP}.iso" ${TEMPMNTDIR} && \
		cp -r ${TEMPMNTDIR}/* ${TEMPISODIR}
	gtest continued $? 'Failed to mount ISO and copy files'
	umount "${TEMPMNTDIR}"
	gtest continued $? "Failed to unmount ${TEMPMNTDIR}"

	cp "${SPECDIR}/isolinux/isolinux.cfg" ${TEMPISODIR}/isolinux/
	cp "${SPECDIR}/isolinux/syslinux.cfg" ${TEMPISODIR}/
	cp "${SPECDIR}/isolinux/boot.msg" ${TEMPISODIR}/isolinux/
	DATE=$(date --utc)
	echo "GNAP-${VERSION}-${GNAP_STAMP} built on ${DATE}" \
		>> "${TEMPISODIR}/isolinux/boot.msg"

	tar cf "gnap-${VERSION}-${GNAP_STAMP}.tar" -C "${TEMPISODIR}" .
	gtest $? 'Failed to create tarball'
	rm "gnap-${VERSION}-${GNAP_STAMP}.iso"

	gbegin '  Creating basefs component'
	tar jcf "gnap-basefs-${VERSION}-${GNAP_STAMP}.tar.bz2" \
		-C "${storedir}/tmp/gnap/livecd-stage2-${SUBARCH}-${GNAP_STAMP}" .
	gtest $? 'Unable to create basefs tarball'
fi

# EXTENSIONS phase
if [[ "${GNAP_MODULES}" -eq 1 ]]; then
	gbegin "${G}[extensions]${N} stage start"
	GRP_PREFIX="${storedir}/builds/${RELTYPE}/grp-${SUBARCH}-${GNAP_STAMP}"
	SPECMODULE="${SPECDIR}/extensions.conf"
	mod_list=$(grep '^extensions:' "${SPECMODULE}" 2>/dev/null);
	mod_list="${mod_list/extensions:/}"
	test -n "${mod_list}"
	gtest $? 'No extension to build'
	for mod_name in $mod_list ; do
		gbegin "  Building ${mod_name} extension"
		mod_useflags=$(grep "^${mod_name}/useflags:" ${SPECMODULE} 2>/dev/null);
		mod_useflags="${mod_useflags/${mod_name}\/useflags:/}"
		mod_packlist=$(grep "^${mod_name}/packlist:" ${SPECMODULE} 2>/dev/null);
		mod_packlist="${mod_packlist/${mod_name}\/packlist:/}"
		mod_cleanup=$(grep "^${mod_name}/cleanup:" ${SPECMODULE} 2>/dev/null);
		mod_cleanup="${mod_cleanup/${mod_name}\/cleanup:/}"
		TEMPCONF="${TEMPDIR}/ext-${mod_name}.conf"
		touch "${TEMPCONF}"
		gtest continued $? 'Failed to create extension temporary conf file'
		cat >> $TEMPCONF <<EOF
target: grp
${DISTCCSPEC}
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${GNAP_STAMP}
version_stamp: ${GNAP_STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/stage3-${SUBARCH}-${GNAP_STAMP}
grp: ${mod_name}
grp/use: ${mod_useflags}
grp/${mod_name}/type: pkgset
grp/${mod_name}/packages: ${mod_packlist}
EOF

		$GNAP_CATALYST_BIN -c "${GNAP_CATALYST_CONF}" -f "${TEMPCONF}" \
			>> "${GNAP_LOGPREFIX}.out" 2>> "${GNAP_LOGPREFIX}.err"
		gtest continued $? "Extension build failed, ${SEELOGFILES}"

		TEMPMODULEDIR="${TEMPDIR}/module_${mod_name}"
		gmkdir "${TEMPMODULEDIR}"
		for pkg in $( ls ${GRP_PREFIX}/${mod_name}/All/*.tbz2 ); do
			tar jxf $pkg -C "${TEMPMODULEDIR}" -p \
				>> "${GNAP_LOGPREFIX}.out" 2>> "${GNAP_LOGPREFIX}.err"
		done
		gtest continued $? 'Failed to unpack extension packages'

		mod_rmlist=""
		for cleanup in ${mod_cleanup} ; do
			cleanup=${cleanup/\.\./}
			mod_rmlist="${mod_rmlist} ${TEMPMODULEDIR}/${cleanup}"
		done
		rm -rf ${mod_rmlist}
		gtest continued $? 'Failed to apply extension cleanup instructions'

		tar jcf "gnapext_${mod_name}-${GNAP_STAMP}.tbz2" \
			-C "${TEMPMODULEDIR}" . \
			>> "${GNAP_LOGPREFIX}.out" 2>> "${GNAP_LOGPREFIX}.err"
		gtest $? 'Failed to build extension file'
	done
fi

cleanup
echo 'Build successful!'
exit 0
